package client.audio;


import java.io.File;

import org.videolan.jvlc.Audio;
import org.videolan.jvlc.JVLC;
import org.videolan.jvlc.MediaDescriptor;
import org.videolan.jvlc.MediaPlayer;
import org.videolan.jvlc.event.MediaPlayerListener;

import client.event.PlayerManagerEvent;
import client.event.PlayerManagerListener;
import client.net.ISessionManager;
import javax.swing.event.EventListenerList;

/**
 * @author primianoc@gmail.com
 *
 */
public class PlayerManager implements MediaPlayerListener
{
	private int _streamLength = 0;

	private boolean _playerPlaying = false;

	private int _startTime = 0;

	private ISessionManager _iSessionManager = null;

	private String _absolutePathName = null;
	private String _streamName = null;

	private MediaPlayer _mediaPlayer = null;
	private JVLC _jvlc = null;
	private Audio _audio = null;
	private MediaDescriptor _mediaDescriptor = null;
	private int _currentTimeMediaPlayer = 0;

	private boolean _startTimeChanging = false;

	private String _labelTime = "00:00:00";

	private EventListenerList _eventListenerList = new EventListenerList();

	private boolean _timeChangedEnabled = true;

	private boolean _waitingData = false;

	private Object _synchPlay = new Object();

	private boolean _closed = false;

	/**
	 * @return the _startTime
	 */
	public int getStartTime()
	{
		return _startTime;
	}

	/**
	 * @return the _waitingData
	 */
	public boolean isWaitingData()
	{
		return _waitingData;
	}

	/**
	 * @return the _labelTime
	 */
	public String getLabelTime()
	{
		return _labelTime;
	}

	public int getCurrentTime()
	{
		return _currentTimeMediaPlayer + _startTime;
	}

	/**
	 * @return the _streamLength
	 */
	public int getStreamLength()
	{
		return _streamLength;
	}


	/**
	 * @return the _streamName
	 */
	public String getStreamName()
	{
		return _streamName;
	}


	/**
	 * @return the _playerPlaying
	 */
	public boolean isPlayerPlaying()
	{
		return _playerPlaying;	
	}

	/**
	 * Costruttore
	 */
	public PlayerManager(String absolutePathName, String streamName,
			int streamLength, ISessionManager iSessionManager) 
	{
		_absolutePathName = absolutePathName;
		_streamName = streamName;
		_streamLength = streamLength;
		_iSessionManager = iSessionManager;

		initJvlc();
	}

	private void initJvlc()
	{
		_jvlc  = new JVLC();
		_audio  = new Audio(_jvlc);
		_mediaDescriptor = new MediaDescriptor(_jvlc, _absolutePathName);
		_mediaPlayer = _mediaDescriptor.getMediaPlayer();
		_mediaPlayer.addListener(this);

	}


	/**
	 * Metodo invocato quando deve essere chiuso il PlayerManager. 
	 * Provoca la terminazione del thread. 
	 */
	public synchronized void close() 
	{
		if(_closed == false)
		{
			_playerPlaying = false;

			_mediaPlayer.stop();	

			_mediaPlayer.release();

			_jvlc.release();

			_jvlc = null;

			_closed  = true;

			synchronized(_synchPlay)
			{
				_synchPlay.notify();
			}
		}
	}

	/**
	 * Metodo invocato per richiedere l'esecuzione del Player
	 */
	public void play()
	{		
		_playerPlaying = true;

		if(_mediaPlayer.isPlaying() == false)
		{
			_mediaPlayer.play();
		}

		_waitingData = false;
	}

	/**
	 * Metodo invocato per richiedere l'arresto del Player
	 */
	public void pause()
	{	
		_playerPlaying = false;
		_mediaPlayer.pause();
	}

	public void stopPlayer()
	{
		_playerPlaying = false;
		_mediaPlayer.stop();
	}

	/**
	 * Metodo invocato per modificare il volume del Player
	 */
	public void setGainLevel(int gainLevel)
	{
		_audio.setVolume(gainLevel);
	}

	/**
	 * @param time the _startTime to set
	 */
	public void setStartTime(int time)
	{	
		if(_startTimeChanging == false)
		{
			/**
			 *  Se il nuovo istante è diverso da quello corrente allora bisogna
			 *  richiedere al gateway un nuovo mediaTime.
			 */
			int fileLength = FileAudio.getInstance().getDurationSeconds(new File(_absolutePathName));

			if(time < _startTime || time > _startTime + fileLength)
			{
				_startTimeChanging = true;

				_mediaPlayer.stop();

				if(_iSessionManager != null && _iSessionManager.sendNewStartTime(time) == true)
				{
					_startTime = time;
				}
				else
				{
					_startTimeChanging = false;

					play();
				}
			}
			else
			{
				setTime((time - _startTime ));
			}
		}
	}


	private void setTime(int time)
	{
		if(_mediaPlayer.getLength() == -1)
		{
			/**
			 * Se la lunghezza del mediaPlayer è -1
			 * allora non è mai stato invocato il 
			 * metodo play. Azzero il volume ed 
			 * invoco play.
			 */
			_audio.toggleMute();
			_mediaPlayer.play();

			try
			{
				Thread.sleep(500);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}

			_mediaPlayer.setTime(time*1000);

			if(_playerPlaying == false)
			{
				_mediaPlayer.pause();
			}

			_audio.toggleMute();

		}
		else
		{
			_mediaPlayer.setTime(time*1000);

			if(_playerPlaying == true && _mediaPlayer.isPlaying() == false)
			{
				_mediaPlayer.play();
			}
		}

	}

	@Override
	public void endReached(MediaPlayer arg0)
	{	
		if(_startTimeChanging == false && _closed == false)
		{
			if(getCurrentTime() >= _streamLength - 3)
			{
				_playerPlaying = false;

				if(_startTime > 0)
				{
					if(_iSessionManager != null)
					{
						_iSessionManager.sendNewStartTime(0);
					}
				}

				_currentTimeMediaPlayer = 0;

				fireEndReached();
			}
			else
			{
				fireWaitData();
				_waitingData = true;
			}

			Runnable runnable = new Runnable()
			{
				private boolean _threadEnable = true;

				public void run()
				{
					while(_threadEnable == true && _closed == false && 
							_mediaPlayer.isPlaying() == false)
					{	
						try
						{
							Thread.sleep(1000);
						} 
						catch (InterruptedException e)
						{
							e.printStackTrace();
						}

						synchronized(_synchPlay)
						{
							/**
							 * Il media player non è in esecuzione. 
							 */
							resetMediaPlayer();

							/**
							 * Lunghezza 
							 * del file target in secondi.	
							 */
							int fileLength = -1;

							try
							{
								fileLength =  FileAudio.getInstance().getDurationSeconds(new File(_absolutePathName));
							}
							catch (Exception e)
							{
								e.printStackTrace();
							}

							if(fileLength > _currentTimeMediaPlayer + 1)
							{		
								if(_playerPlaying == true)
								{
									setTime(_currentTimeMediaPlayer);
								}

								_threadEnable = false;
							}
						}
					}
					_waitingData = false;
				}	
			};
			Thread threadAnon = new Thread(runnable);
			threadAnon.start();

		}
	}

	@Override
	public void timeChanged(MediaPlayer mediaPlayer, long newTime)
	{
		/** 
		 * newTime è espresso in microsecondi, quindi per convertirlo
		 * in secondi bisogna dividerlo per 1000000 (un milione).
		 */
		if(_currentTimeMediaPlayer != ((int)newTime/1000000) &&
				_timeChangedEnabled  == true && ((int)newTime/1000000) != 0)
		{
			_currentTimeMediaPlayer = (int)newTime/1000000;

			fireTimeChanged();
		}
	}

	//Metodi che permettono ai listener di registrarsi per intercettare
	//gli eventi relativi al passaggio di stato del Player
	public void addPlayerManagerListener(PlayerManagerListener listener)
	{
		_eventListenerList.add(PlayerManagerListener.class, listener);
		fireTimeChanged();

	}

	public void removePlayerManagerListener(PlayerManagerListener listener)
	{
		_eventListenerList.remove(PlayerManagerListener.class, listener);
	}

	/**
	 * Lancia l'evento che indica all'interfaccia grafica il cambiamento del player
	 */
	private void fireTimeChanged()
	{
		PlayerManagerEvent event = new PlayerManagerEvent(this);
		Object[] listenersArray = _eventListenerList.getListenerList();

		for(int i = listenersArray.length - 2; i >= 0; i = i - 2)
		{
			if(listenersArray[i] == PlayerManagerListener.class)
			{
				((PlayerManagerListener)listenersArray[i+1])
				.timeChanged(event);
			} 
		}

	}

	private void fireEndReached()
	{
		PlayerManagerEvent event = new PlayerManagerEvent(this);

		Object[] listenersArray = _eventListenerList.getListenerList();

		for(int i = listenersArray.length - 2; i >= 0; i = i - 2)
		{
			if(listenersArray[i] == PlayerManagerListener.class)
			{
				((PlayerManagerListener)listenersArray[i+1])
				.endReached(event);
			} 
		}

	}

	private void fireWaitData()
	{
		PlayerManagerEvent event = new PlayerManagerEvent(this);

		Object[] listenersArray = _eventListenerList.getListenerList();

		for(int i = listenersArray.length - 2; i >= 0; i = i - 2)
		{
			if(listenersArray[i] == PlayerManagerListener.class)
			{
				((PlayerManagerListener)listenersArray[i+1])
				.waitData(event);
			} 
		}

	}

	public void updatePlayer(int startTime)
	{
		/**
		 * Metodo invocato quando scatta un evento NewStream.
		 */
		_startTime = startTime;

		Runnable runnable = new Runnable()
		{
			private boolean _threadEnable = true;

			public void run()
			{

				while(_threadEnable == true && _closed == false && 
						_mediaPlayer.isPlaying() == false )
				{
					try
					{
						Thread.sleep(1000);
					} 
					catch (InterruptedException e)
					{
						e.printStackTrace();
					}

					synchronized(_synchPlay)
					{
						/**
						 * Il media player non è in esecuzione. 
						 */
						resetMediaPlayer();

						/**
						 * Lunghezza 
						 * del file target in secondi.	
						 */
						int fileLength = -1;

						try
						{
							fileLength =  FileAudio.getInstance().getDurationSeconds(new File(_absolutePathName));
						}
						catch (Exception e)
						{
							e.printStackTrace();
						}

						if(fileLength > 1)
						{		
							if(_playerPlaying == true)
							{
								_mediaPlayer.play();

							}	

							_threadEnable = false;
						}
					}
				}
				_waitingData = false;
			}	
		};
		Thread threadAnon = new Thread(runnable);
		threadAnon.setName("Thread Anonimo creato nel metodo updatePlayer.");
		threadAnon.start();

		_startTimeChanging = false;
	}

	private void resetMediaPlayer()
	{
		if(_closed == false)
		{
			_mediaPlayer.release();
			_mediaDescriptor = new MediaDescriptor(_jvlc, _absolutePathName);
			_mediaPlayer = _mediaDescriptor.getMediaPlayer();
			_mediaPlayer.addListener(this);
		}

	}

	@Override
	public void errorOccurred(MediaPlayer arg0)
	{
	}


	@Override
	public void paused(MediaPlayer arg0)
	{

	}


	@Override
	public void playing(MediaPlayer arg0)
	{
	}


	@Override
	public void positionChanged(MediaPlayer arg0)
	{

	}


	@Override
	public void stopped(MediaPlayer arg0)
	{
	}
}
